﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetadataBasedConfig.Framework.PropertyExtend;
using MetadataBasedConfig.EntityModel;
using MetadataBasedConfig.Framework;
using MetadataBasedConfig.Framework.Model;
using System.ComponentModel;
using MetadataBasedConfig.Framework.Validator;

namespace MetadataBasedConfig.EntityModel.Entity
{
    public abstract class ConfigurableEntityObject : BasicEntityObject, IConfigurable
    {
        #region Fields

        #endregion

        #region Implement IPropertyExtendable interface

        private IEnumerable<KeyValuePair<string, object>> __ExtendProperties;
        IEnumerable<KeyValuePair<string, object>> IPropertyExtendable.ExtendProperties
        {
            get
            {
                if (__ExtendProperties== null && ConfigMetaConfigInfoDatasLoader == null)
                {
                    ConfigMetaConfigInfoDatasLoader(this);
                }
                return __ExtendProperties;
            } 
        }
                 
        void IPropertyExtendable.SetExtendProperty(string propertyName, object value)
        {
            var configurable = (IConfigurable)this;
            var configInfo = (from c in configurable.ConfigInfoesInst.IncludeStaticConstraint() where c.Content==propertyName select c).FirstOrDefault();
            if (configInfo == null)
                return;

            ConfigInfoPropertyValidatContext context = new ConfigInfoPropertyValidatContext(value, SourceType.CLR);
            foreach (var configInfoProperty in configInfo.ConfigInfoProperties)
            {
                configInfoProperty.Validate(context);
            }
            if (context.IsValid)
            {
                var exProperty = (from ep in configurable.ExtendProperties where ep.Key == propertyName select ep).FirstOrDefault();
                var configData = configurable.ConfigInfoDatasInst.Where(cd => cd.ConfigInfo.Content == propertyName).FirstOrDefault();
                if (exProperty.Key == propertyName && configData != null)
                {
                    configData.DataValue = context.TargetValue;
                }
                else
                {
                    configData = Helper.ConfigMetadataProvider.CreateMetadata<IConfigMetaConfigInfoData, ConfigMetaConfigData>();
                    configData.ConfigInfo = configInfo;
                    configData.DataValue = context.TargetValue;
                    configData.DataID = configurable.RecID;
                    Helper.ConfigMetadataProvider.AddMetadata<IConfigMetaConfigInfoData,ConfigMetaConfigData>(configData);
                }
                var extendProperties = configurable.ExtendProperties.Where(ep => ep.Key != propertyName).ToList();
                extendProperties.Add(new KeyValuePair<string, object>(propertyName, value));
                __ExtendProperties = extendProperties;
            }
            else
            {
                throw new ArgumentException("value is not valid!");
            }
        }

        object IPropertyExtendable.GetExtendProperty(string propertyName)
        {
            return (from ep in ((IConfigurable)this).ExtendProperties where ep.Key == propertyName select ep.Value).FirstOrDefault();
        }

        #endregion

        #region Implement IConfigurable interface

        private IEnumerable<IConfigMetaConfigInfo> __ConfigInfoesInst = null;
        IEnumerable<IConfigMetaConfigInfo> IConfigurable.ConfigInfoesInst
        {
            get
            {
                return __ConfigInfoesInst;
            } 
        }

        private IEnumerable<IConfigMetaConfigInfoData> __ConfigInfoDatasInst = null;
        IEnumerable<IConfigMetaConfigInfoData> IConfigurable.ConfigInfoDatasInst
        {
            get 
            {
                return __ConfigInfoDatasInst;
            } 
        }

        private IConfigMeta __ConfigMeta = null;
        IConfigMeta IConfigurable.ConfigMeta
        {
            get
            {
                return __ConfigMeta;
            } 
        }
         
        Int64 IConfigurable.RecID
        {
            get
            {
                return GetRecID();
            }
        }

        #endregion

        #region Config Info Datas Loader

        internal Action<ConfigurableEntityObject> ConfigMetaConfigInfoDatasLoader
        {
            get;
            set;
        }

        #endregion

        #region Other Methods

        internal virtual void SetConfigInfoDatas(IConfigMeta configMeta, 
                                                 IEnumerable<IConfigMetaConfigInfo> configInfos,
                                                 IEnumerable<IConfigMetaConfigInfoData> configInfoDatas,
                                                 IEnumerable<KeyValuePair<string, object>> extendProperties)
        {
            __ConfigMeta = configMeta;
            __ExtendProperties = extendProperties;
            __ConfigInfoesInst = configInfos;
            __ConfigInfoDatasInst = configInfoDatas;
        }

        public virtual void LoadConfigInfoDatas()
        {
            if (ConfigMetaConfigInfoDatasLoader != null)
            {
                ConfigMetaConfigInfoDatasLoader(this);
            }
        }

        #endregion

        #region Abstract Methods

        protected abstract Int64 GetRecID();

        #endregion

    }
}
